﻿using DataProcess4Net48.Views;
using log4net;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using log4net.Core;
using DataProcess.Core.Model;
using DataProcess.Interface;
using DataProcess.Core.Tools;
using DataProcess.Core.DataAccess;
using DataProcess.Core.Model.Tqms;
using DataProcess.Core.Enums;
using DataProcess.Interface.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;

namespace DataProcess4Net48.CustomUserControl
{
    public partial class EquipmengControl : UserControl
    {
        public Equipment Equipment { get; private set; }
        public Assembly DataProcessingAssembly { get; private set; }

        public IDataProcessing DataProcessingInstance { get; private set; }

        public Assembly DataDriveAssembly { get; private set; }

        public IDataDrive DataDriveInstance { get; private set; }

        public Dictionary<string, Assembly> DataTransmissionAssemblyMap { get; private set; }

        public Dictionary<string, IDataTransmission> DataTransmisionInstanceMap { get; private set; }

        public List<LogItem> LogList { get; set; }

        readonly ILog Logger = LogManager.GetLogger("Info");

        private bool lastRunningState = false;

        public EquipmengControl(Equipment equipment)
        {
            Equipment = equipment;
            LogList = new List<LogItem>();
            InitializeComponent();
            Init();
            LabelMsg.Text = "";
        }

        #region 初始化

        private void Init()
        {
            if (Equipment == null)
            {
                return;
            }

            LbName.Text = Equipment.Name;

            InitDataProcessing();
            InitDataDrive();
            InitDataTransmission();
        }

        /// <summary>
        /// 初始化数据处理
        /// </summary>
        private void InitDataProcessing()
        {
            if (string.IsNullOrEmpty(Equipment.DataProcessingFile))
            {
                return;
            }
            string dataProcessingPath = AppDomain.CurrentDomain.BaseDirectory + $@"{ReflectionMark.ProcessingMark}\{Equipment.DataProcessingFile}";

            if (!File.Exists(dataProcessingPath))
            {
                return;
            }

            DataProcessingAssembly = Assembly.LoadFile(dataProcessingPath);

            Type dataProcessingType = DataProcessingAssembly.GetType($"{DataProcessingAssembly.GetName().Name}.{ReflectionMark.ProcessingMark}");
            if (dataProcessingType == null)
            {
                return;
            }
            DataProcessingInstance = (IDataProcessing)Activator.CreateInstance(dataProcessingType);
            LbDataProcessing.Text = DataProcessingInstance.Name;
        }

        /// <summary>
        /// 初始化数据驱动
        /// </summary>
        private void InitDataDrive()
        {
            if (string.IsNullOrEmpty(Equipment.DataDriveFile))
            {
                return;
            }
            string dataDrivePath = AppDomain.CurrentDomain.BaseDirectory + $@"{ReflectionMark.DriveMark}\{Equipment.DataDriveFile}";

            if (!File.Exists(dataDrivePath))
            {
                return;
            }

            DataDriveAssembly = Assembly.LoadFile(dataDrivePath);

            Type dataDriveType = DataDriveAssembly.GetType($"{DataDriveAssembly.GetName().Name}.{ReflectionMark.DriveMark}");
            if (dataDriveType == null)
            {
                return;
            }

            Type configType = DataDriveAssembly.GetType($"{DataDriveAssembly.GetName().Name}.{ReflectionMark.ConfigMark}");
            if (configType == null)
            {
                return;
            }

            var config = Equipment.DataDriveConfig.FromJson(configType);
            if (config == null)
            {
                return;
            }

            DataDriveInstance = (IDataDrive)Activator.CreateInstance(dataDriveType, config);
            DataDriveInstance.Started += EquipmengControl_Started;
            DataDriveInstance.Stoped += EquipmengControl_Stoped;
            DataDriveInstance.InfoNoticed += EquipmengControl_InfoNoticed;
            DataDriveInstance.DataRecived += EquipmengControl_DataRecived;
            DataDriveInstance.WarnNoticed += EquipmengControl_WarnNoticed;
            DataDriveInstance.ErrorNoticed += EquipmengControl_ErrorNoticed;

            if (Equipment.AutoRun)
            {
                DataDriveInstance.Start();
            }

            LbDataDrive.Text = DataDriveInstance.Name;
        }

        /// <summary>
        /// 初始化数据传输
        /// </summary>
        private void InitDataTransmission()
        {
            DataTransmissionAssemblyMap = new Dictionary<string, Assembly>();
            DataTransmisionInstanceMap = new Dictionary<string, IDataTransmission>();

            foreach (var transConfig in Equipment.DataTransmissionConfigs)
            {
                string transmissionPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + ReflectionMark.TransmissionMark, transConfig.DriveFileName);
                if (!File.Exists(transmissionPath))
                {
                    continue;
                }

                Assembly assem;
                IDataTransmission transInstance;
                Type ITransmission = typeof(IDataTransmission);
                Type ITransmissionConfig = typeof(IDataTransmissionConfig);
                if (DataTransmissionAssemblyMap.ContainsKey(transConfig.DriveFileName))
                {
                    assem = DataTransmissionAssemblyMap[transConfig.DriveFileName];
                    transInstance = DataTransmisionInstanceMap[transConfig.DriveFileName];
                }
                else
                {
                    assem = Assembly.LoadFile(transmissionPath);
                    Type driveType = assem.GetTypes().SingleOrDefault(it => ITransmission.IsAssignableFrom(it));
                    if (driveType == null) continue;
                    Type configType = assem.GetTypes().SingleOrDefault(it => ITransmissionConfig.IsAssignableFrom(it));
                    if (configType == null) continue;
                    var config = transConfig.DriveConfig.FromJson(configType);
                    if (config == null) continue;
                    transInstance = (IDataTransmission)Activator.CreateInstance(driveType, config);
                    if (transInstance == null) continue;
                    transInstance.InfoNoticed += EquipmengControl_InfoNoticed;
                    transInstance.WarnNoticed += EquipmengControl_WarnNoticed;
                    transInstance.ErrorNoticed += EquipmengControl_ErrorNoticed;
                    transInstance.SaveTransFaultedData += TransInstance_SaveTransFaultedData;
                    transInstance.GetTransFaultedData += TransInstance_GetTransFaultedData;
                    transInstance.AfterTransferFaultedData += TransInstance_AfterTransferFaultedData;

                    DataTransmissionAssemblyMap.Add(transConfig.DriveFileName, assem);
                    DataTransmisionInstanceMap.Add(transConfig.DriveFileName, transInstance);
                }
            }
        }

        /// <summary>
        /// 获取当前传输失败的数据
        /// </summary>
        /// <param name="reTryCount">重试次数</param>
        /// <param name="transmissionMark">传输通道名称</param>
        /// <returns>是否获取失败数据成功</returns>
        private List<FailedData> TransInstance_GetTransFaultedData(int reTryCount, string transmissionMark)
        {
            DbContext<FailedData> context = new DbContext<FailedData>();
            if (reTryCount == 0)
            {
                return context.Get(it => it.DriveType == transmissionMark && it.EquipmentName == Equipment.Name);
            }
            else
            {
                return context.Get(it => it.DriveType == transmissionMark && it.EquipmentName == Equipment.Name && it.ReTryCount < reTryCount);
            }
        }

        /// <summary>
        /// 保存传输失败数据
        /// </summary>
        /// <param name="faileddata">失败数据对象</param>
        /// <returns>是否保存至数据库成功</returns>
        private bool TransInstance_SaveTransFaultedData(FailedData faileddata)
        {
            if (faileddata == null) return false;
            faileddata.EquipmentName = Equipment.Name;
            return faileddata.Save();
        }

        /// <summary>
        /// 失败数据传输之后需要执行的逻辑
        /// </summary>
        /// <param name="delList">需要删除的失败数据列表</param>
        /// <param name="updList">需要更新的失败数据列表</param>
        private void TransInstance_AfterTransferFaultedData(List<FailedData> delList, List<FailedData> updList)
        {
            delList.DeleteByIds(delList.Select(it => it.ID.ToString()).ToArray());
            updList.UpdateColumns(it => it.ReTryCount);
        }

        #endregion

        #region 窗体事件

        private void BtnStart_Click(object sender, EventArgs e)
        {
            DataDriveInstance.Start();
        }

        private void BtnStop_Click(object sender, EventArgs e)
        {
            DataDriveInstance.Stop();
        }

        private void BtnLog_Click(object sender, EventArgs e)
        {
            //查看日志
            LogForm logForm = new LogForm(LogList);
            logForm.Show();

            var location = this.Parent?.Parent?.Location;
            if (location.HasValue)
            {
                var point = new Point(location.Value.X, location.Value.Y + Program.Instance.Size.Height);
                logForm.Location = point;
            }
        }

        private void BtnState_Click(object sender, EventArgs e)
        {
            ResetState();
            SetLabelMsg(Level.Info, string.Empty);
        }

        private void BTN_Setting_Click(object sender, EventArgs e)
        {
            if (sender is Button btn)
            {
                btn.Enabled = false;
                try
                {
                    EquipmentDetailsForm frm = new EquipmentDetailsForm(Equipment, DataEditType.Modify)
                    {
                        Text = "设备明细"
                    };
                    frm.Updated += EquipmentUpdated;
                    frm.ShowDialog(this);
                }
                finally
                {
                    btn.Enabled = true;
                }
            }
        }

        private void EquipmentUpdated(Equipment equipment)
        {
            //比较传入的Equipment和原有的是否一致
            if (equipment.ToJson() != Equipment.ToJson())
            {
                DataDriveInstance?.Dispose();
                Equipment = equipment;
                Init();
            }
        }

        #endregion

        #region 数据驱动以及数据处理的事件绑定以及方法调用

        private TransferDataItem DataProcessing(string rawData, params object[] obj)
        {
            TransferDataItem transferDataItem = new TransferDataItem()
            {
                Raw = rawData,
            };

            try
            {
                IDataOutput middleData = DataProcessingInstance.Processing(rawData);
                if (middleData == null) return default;
                transferDataItem.Middle = middleData;
                switch (middleData.InstrumentType)
                {
                    case InstrumentType.XRF:
                    case InstrumentType.XRD:
                        var xrayModel = middleData as XRayOutputModel;
                        if (xrayModel == null) break;
                        //XRF获取映射关系
                        Dictionary<string, string> mapper = Program.SystemSetting?.AnalysisMappingList?.ToDictionary(it => it.SourceName, it => it.TargetName);
                        transferDataItem.Processed = new TqmsXrfOutputModel()
                        {
                            comment = Program.SystemSetting.TargetName,
                            plantCode = Program.SystemSetting.TargetCode,
                            plantName = Program.SystemSetting.TargetName,
                            fileName = DataDriveInstance.SourceName,
                            measDateFormat = xrayModel.MeasureTime.ToString("yyyyMMdd"),
                            measDateStr = xrayModel.MeasureTime.ToString("yyyy-MM-dd HH:mm:ss"),
                            sampleName = xrayModel.SampleName,
                            analyzeApplicationName = xrayModel.AnalyzeApplicationName,
                            ygyResultDetails = xrayModel.Eelements.Where(it => mapper.ContainsKey(it.Key)).Select(it => new QimsAssayItemBaseDetailDto
                            {
                                compName = mapper[it.Key],
                                conc = it.Value
                            }).ToList()
                        }.ToJson();
                        break;
                    case InstrumentType.Calorimetry:
                    case InstrumentType.Analyzer:
                        var dataBaseModel = middleData as DataBaseOutputModel;
                        if (dataBaseModel == null) break;
                        //找出数据中最大的时间点 更新数据库
                        string maxDate = dataBaseModel.Rows.ToList().OrderBy(dict => dict["createDate"]).Last()["createDate"];
                        var jobject = JObject.Parse(Equipment.DataDriveConfig);
                        if (jobject.ContainsKey("Date"))
                        {
                            jobject["Date"] = maxDate;
                        }
                        IsoDateTimeConverter dtConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy/MM/dd HH:mm:ss" };
                        Equipment.DataDriveConfig = JsonConvert.SerializeObject(jobject, dtConverter);
                        DbContext<Equipment> DBContext = new DbContext<Equipment>();
                        DBContext.CurrentDb.AsUpdateable(Equipment).UpdateColumns(it => it.DataDriveConfig).ExecuteCommand();

                        //信息公司接口,看接口需要传入的数据对象的构成
                        if (middleData.InstrumentType == InstrumentType.Calorimetry)
                        {
                            TqmsOutputModelList<TqmsCalorimetryDTO> outputCa = new TqmsOutputModelList<TqmsCalorimetryDTO>();
                            foreach (var item in dataBaseModel.Rows)
                            {
                                //量热仪
                                outputCa.Add(new TqmsCalorimetryDTO()
                                {
                                    autoNum = item["autoNum"],
                                    createDate = item["createDate"].Replace("/", "-"),
                                    manualNum = item["manualNum"],
                                    plantCode = Program.SystemSetting.TargetCode,
                                    plantName = Program.SystemSetting.TargetName,
                                    dataSource = Equipment.Name,
                                    qb_ad = item["qb_ad"],
                                    qnet_ad = "",
                                    qnet_ar = "",
                                    testItme = item["testItme"].Replace("/", "-")
                                });
                            }
                            //赋值输出数据
                            transferDataItem.Processed = outputCa.ToJson();
                            break;
                        }

                        if (middleData.InstrumentType == InstrumentType.Analyzer)
                        {
                            TqmsOutputModelList<TqmsanAlysisDTO> outputAl = new TqmsOutputModelList<TqmsanAlysisDTO>();
                            foreach (var item in dataBaseModel.Rows)
                            {
                                //定硫仪
                                outputAl.Add(new TqmsanAlysisDTO()
                                {
                                    autoNum = item["autoNum"],
                                    createDate = item["createDate"].Replace("/", "-"),
                                    manualNum = item["manualNum"],
                                    plantCode = Program.SystemSetting.TargetCode,
                                    plantName = Program.SystemSetting.TargetName,
                                    dataSource = Equipment.Name,
                                    st_ad = item["stad"],
                                    testItme = item["testItme"].Replace("/", "-")
                                });
                            }
                            //赋值输出数据                            
                            transferDataItem.Processed = outputAl.ToJson();
                            break;
                        }
                        break;
                    case InstrumentType.NJY:
                        dataBaseModel = middleData as DataBaseOutputModel;
                        if (dataBaseModel == null) break;
                        if (dataBaseModel.Rows.Count == 0)
                        {
                            SetWarn("要传输的数据项为空，请检查SQL语句中要查询的项是否正确");
                            break;
                        }

                        Dictionary<string, string> itemDic = dataBaseModel.Rows[0];
                        if (!itemDic.ContainsKey("SAMPLE_NAME"))
                        {
                            SetWarn("查询项中没有 SAMPLE_NAME 列名，请检查SQL语句");
                        }

                        if (!itemDic.ContainsKey("MEAS_DATE_STR"))
                        {
                            SetWarn("查询项中没有 MEAS_DATE_STR 列名，请检查SQL语句");
                        }

                        OutputWebApiResult outputResult = new OutputWebApiResult
                        {
                            hardwareType = InstrumentType.NJY.ToString(),
                            plantCode = Program.SystemSetting.TargetCode,
                            plantName = Program.SystemSetting.TargetName,
                            sampleName = itemDic["SAMPLE_NAME"],
                            measDateStr = itemDic["MEAS_DATE_STR"],
                            comment = Program.SystemSetting.TargetName,
                            hardwareCenterItems = new List<OutputWebApiCenterItem>()
                        };

                        foreach (KeyValuePair<string, string> kv in itemDic)
                        {
                            outputResult.hardwareCenterItems.Add(new OutputWebApiCenterItem
                            {
                                compName = kv.Key,
                                conc = kv.Value,
                                mainId = outputResult.id
                            });
                        }

                        //更新数据库设置表中最大的时间点
                        jobject = JObject.Parse(Equipment.DataDriveConfig);
                        if (jobject.ContainsKey("Date"))
                        {
                            jobject["Date"] = itemDic["MEAS_DATE_STR"];
                        }
                        dtConverter = new IsoDateTimeConverter { DateTimeFormat = "yyyy/MM/dd HH:mm:ss" };
                        Equipment.DataDriveConfig = JsonConvert.SerializeObject(jobject, dtConverter);
                        DBContext = new DbContext<Equipment>();
                        DBContext.CurrentDb.AsUpdateable(Equipment).UpdateColumns(it => it.DataDriveConfig).ExecuteCommand();

                        transferDataItem.Processed = outputResult.ToJson();
                        break;
                    default:
                        break;
                }
            }
            catch (Exception ex)
            {
                SetError($"处理数据失败,详情如下：{ex.Message}");
            }
            return transferDataItem;
        }

        /// <summary>
        /// 数据传输
        /// </summary>
        /// <param name="transDataItem">传输数据对象</param>
        private void SendOutputInfo(TransferDataItem transDataItem)
        {
            foreach (var transConfig in Equipment.DataTransmissionConfigs)
            {
                if (string.IsNullOrEmpty(transConfig.DriveFileName)) continue;

                if (DataTransmisionInstanceMap.ContainsKey(transConfig.DriveFileName))
                {
                    try
                    {
                        DataTransmisionInstanceMap[transConfig.DriveFileName].TransferData(transDataItem);
                    }
                    catch (Exception ex)
                    {
                        SetError(ex.Message);
                    }
                }
            }
        }

        #endregion

        #region 日志及界面状态更改

        private void SetInfo(string msg)
        {
            Logger.Info(msg);
            AppendLogToList(msg, Level.Info);
        }

        private void SetWarn(string warn)
        {
            Logger.Warn(warn);
            AppendLogToList(warn, Level.Warn);
            if (BtnState.Appearance.BackColor != Color.ForestGreen) return;
            SetButtonState(Level.Warn);
            SetLabelMsg(Level.Warn, warn);
        }

        private void SetError(string error)
        {
            Logger.Error(error);
            AppendLogToList(error, Level.Error);
            SetButtonState(Level.Error);
            SetLabelMsg(Level.Error, error);
        }

        private void EquipmengControl_InfoNoticed(string info)
        {
            SetInfo(info);
        }

        private void EquipmengControl_WarnNoticed(string warn)
        {
            SetWarn(warn);
        }
        private void EquipmengControl_ErrorNoticed(string error)
        {
            SetError(error);
        }

        private void ResetState()
        {
            if (DataDriveInstance == null) return;

            if (DataDriveInstance.Running)
            {
                SetButtonState(Level.Info);
            }
            else
            {
                SetButtonState(Level.Off);
            }
        }

        private void AppendLogToList(string msg, Level level)
        {
            if (LogList.Count == 300)
            {
                LogList.RemoveAt(299);
            }
            LogList.Insert(0, new LogItem { Time = DateTime.Now, Level = level.Name, Message = msg });
        }

        private void SetButtonState(Level level)
        {
            Color color = GetColor(level);
            if (BtnState.InvokeRequired)
            {
                BtnState.Invoke(new MethodInvoker(delegate
                {
                    BtnState.Appearance.BackColor = color;
                }));
            }
            else
            {
                BtnState.Appearance.BackColor = color;
            }
        }

        private void SetLabelRunning(bool onoff)
        {
            if (LbRunning.InvokeRequired)
            {
                LbRunning.Invoke(new MethodInvoker(delegate
                {
                    if (onoff)
                    {
                        LbRunning.Text = "运行中";
                        LbRunning.ForeColor = Color.ForestGreen;
                    }
                    else
                    {
                        LbRunning.Text = "待运行";
                        LbRunning.ForeColor = Color.Gray;
                    }
                }));
            }
            else
            {
                if (onoff)
                {
                    LbRunning.Text = "运行中";
                    LbRunning.ForeColor = Color.ForestGreen;
                }
                else
                {
                    LbRunning.Text = "待运行";
                    LbRunning.ForeColor = Color.Gray;
                }
            }
        }

        private void SetLabelMsg(Level level, string msg)
        {
            Color color = GetColor(level);

            if (LabelMsg.InvokeRequired)
            {
                LabelMsg.Invoke(new MethodInvoker(delegate
                {
                    if (level == Level.Info)
                    {
                        LabelMsg.Text = "";
                    }
                    else
                    {
                        LabelMsg.Text = msg;
                        LabelMsg.ForeColor = color;
                    }
                }));
            }
            else
            {
                if (level == Level.Info)
                {
                    LabelMsg.Text = "";
                }
                else
                {
                    LabelMsg.Text = msg;
                    LabelMsg.ForeColor = color;
                }
            }
        }

        private Color GetColor(Level level)
        {
            Color color = Color.White;

            switch (level.Name)
            {
                case "INFO":
                    color = Color.ForestGreen;
                    break;
                case "WARN":
                    color = Color.FromArgb(255, 249, 169, 37);
                    break;
                case "ERROR":
                    color = Color.FromArgb(255, 244, 67, 54);
                    break;
                case "OFF":
                    color = SystemColors.ActiveBorder;
                    break;
            }
            return color;
        }

        public void UpdateRunningState()
        {
            if (DataDriveInstance == null) return;

            if (lastRunningState == DataDriveInstance.Running) return;

            lastRunningState = DataDriveInstance.Running;

            SetButtonState(lastRunningState ? Level.Info : Level.Off);
            SetLabelRunning(lastRunningState);
        }


        #endregion

        #region 需要绑定到数据驱动事件的方法

        private void EquipmengControl_Started()
        {
            SetInfo("数据驱动服务已启动");
            ResetState();
        }

        private void EquipmengControl_Stoped()
        {
            SetInfo("数据驱动服务已停止");
        }

        private void EquipmengControl_DataRecived(string content, params object[] obj)
        {
            if (DataProcessingInstance == null)
            {
                SetError("数据处理实例为null");
                return;
            }
            if (string.IsNullOrEmpty(content))
            {
                SetWarn("读取到的数据为空字符串");
                return;
            }

            SetInfo("接收到数据，准备处理...");
            var transferDataItem = DataProcessing(content, obj);
            SetInfo($"数据已经处理完毕");
            SetInfo("开始进行数据传输...");
            SendOutputInfo(transferDataItem);
            SetInfo("数据传输完毕");
        }

        #endregion

    }
}
